/**
 * @author Don Griffin
 * 
 * This is a base class for more advanced "simlets" (simulated servers). A
 * simlet is asked to provide a response given a {@link Ext.ux.ajax.SimXhr}
 * instance.
 */
Ext.define('Ext.ux.ajax.Simlet', function() {
	var urlRegex = /([^?#]*)(#.*)?$/, dateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/, intRegex = /^[+-]?\d+$/, floatRegex = /^[+-]?\d+\.\d+$/;

	function parseParamValue(value) {
		var m;

		if (Ext.isDefined(value)) {
			value = decodeURIComponent(value);

			if (intRegex.test(value)) {
				value = parseInt(value, 10);
			} else if (floatRegex.test(value)) {
				value = parseFloat(value);
			} else if (!!(m = dateRegex.test(value))) {
				value = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4],
						+m[5], +m[6]));
			}
		}

		return value;
	}

	return {
		alias : 'simlet.basic',

		isSimlet : true,

		responseProps : ['responseText', 'responseXML', 'status', 'statusText'],

		/**
		 * @cfg {Number} responseText
		 */

		/**
		 * @cfg {Number} responseXML
		 */

		/**
		 * @cfg {Object} responseHeaders
		 */

		/**
		 * @cfg {Number} status
		 */
		status : 200,

		/**
		 * @cfg {String} statusText
		 */
		statusText : 'OK',

		constructor : function(config) {
			Ext.apply(this, config);
		},

		doGet : function(ctx) {
			var me = this, ret = {};

			Ext.each(me.responseProps, function(prop) {
						if (prop in me) {
							ret[prop] = me[prop];
						}
					});

			return ret;
		},

		doRedirect : function(ctx) {
			return false;
		},

		/**
		 * Performs the action requested by the given XHR and returns an object
		 * to be applied on to the XHR (containing `status`, `responseText`,
		 * etc.). For the most part, this is delegated to `doMethod` methods on
		 * this class, such as `doGet`.
		 * 
		 * @param {Ext.ux.ajax.SimXhr}
		 *            xhr The simulated XMLHttpRequest instance.
		 * @returns {Object} The response properties to add to the
		 *          XMLHttpRequest.
		 */
		exec : function(xhr) {
			var me = this, ret = {}, method = 'do'
					+ Ext.String.capitalize(xhr.method.toLowerCase()), // doGet
			fn = me[method];

			if (fn) {
				ret = fn.call(me, me.getCtx(xhr.method, xhr.url, xhr));
			} else {
				ret = {
					status : 405,
					statusText : 'Method Not Allowed'
				};
			}

			return ret;
		},

		getCtx : function(method, url, xhr) {
			return {
				method : method,
				params : this.parseQueryString(url),
				url : url,
				xhr : xhr
			};
		},

		openRequest : function(method, url, options, async) {
			var ctx = this.getCtx(method, url), redirect = this.doRedirect(ctx), xhr;

			if (redirect) {
				xhr = redirect;
			} else {
				xhr = new Ext.ux.ajax.SimXhr({
							mgr : this.manager,
							simlet : this,
							options : options
						});
				xhr.open(method, url, async);
			}

			return xhr;
		},

		parseQueryString : function(str) {
			var m = urlRegex.exec(str), ret = {}, key, value, i, n;

			if (m && m[1]) {
				var pair, parts = m[1].split('&');

				for (i = 0, n = parts.length; i < n; ++i) {
					if ((pair = parts[i].split('='))[0]) {
						key = decodeURIComponent(pair.shift());
						value = parseParamValue((pair.length > 1) ? pair
								.join('=') : pair[0]);

						if (!(key in ret)) {
							ret[key] = value;
						} else if (Ext.isArray(ret[key])) {
							ret[key].push(value);
						} else {
							ret[key] = [ret[key], value];
						}
					}
				}
			}

			return ret;
		},

		redirect : function(method, url, params) {
			switch (arguments.length) {
				case 2 :
					if (typeof url == 'string') {
						break;
					}
					params = url;
					// fall...
				case 1 :
					url = method;
					method = 'GET';
					break;
			}

			if (params) {
				url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
			}
			return this.manager.openRequest(method, url);
		}
	};
}());
